package com.corpgovernment.organization.service.impl;

import static com.corpgovernment.api.basic.enums.LoginType.ACCOUNT;
import static com.corpgovernment.api.basic.enums.LoginType.SMS;
import static com.corpgovernment.api.basic.enums.SourceEnum.PC;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.ACCOUNT_DISABLED;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.ILLEGAL_LOGIN;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.NOTFIND;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.NO_ADMIN_PERMISSION;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.PWD_INCORRECT;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.PWD_UPDATE_FAIL;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.USERNAME_INCORRECT;
import static com.corpgovernment.api.basic.metadata.ELoginStatus.USERNAME_NULL;
import static com.corpgovernment.common.enums.SystemType.zhongjin;
import static com.corpgovernment.organization.enumm.ERedisKeyType.USER_REGISTER_VALID_TOKEN;
import static com.corpgovernment.organization.service.MsgService.TEMPLATE_VARIABLE_PASSWORD_NAME;
import static com.corpgovernment.permission.config.PermissionFilter.DATA_PERMISSION_ORG_ID_LIST;

import java.security.SecureRandom;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang.RandomStringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.corpgovernment.api.basic.dto.OrgDTO;
import com.corpgovernment.api.basic.enums.LoginType;
import com.corpgovernment.api.basic.enums.SourceEnum;
import com.corpgovernment.api.basic.metadata.ELoginStatus;
import com.corpgovernment.api.basic.request.MbUserDeviceRequest;
import com.corpgovernment.api.basic.request.UpdatePasswordRequest;
import com.corpgovernment.api.basic.request.UserLoginRequest;
import com.corpgovernment.api.basic.vo.MbUserDeviceVo;
import com.corpgovernment.api.basic.vo.MbUserLoginRecordVo;
import com.corpgovernment.api.basic.vo.MbUserRegisterIdentityVO;
import com.corpgovernment.api.basic.vo.MbUserRegisterRequestVO;
import com.corpgovernment.api.basic.vo.UserLoginResponseVo;
import com.corpgovernment.api.messageadvice.enums.MsgBizType;
import com.corpgovernment.api.organization.bo.GetUserInfoRequestBO;
import com.corpgovernment.api.organization.enums.LoginTypeEnum;
import com.corpgovernment.api.organization.model.message.SendMsgRequest;
import com.corpgovernment.api.organization.model.user.login.MbUserLoginVo;
import com.corpgovernment.api.organization.model.user.login.PageByConditionRequest;
import com.corpgovernment.api.organization.model.user.login.UserInfoType;
import com.corpgovernment.api.organization.model.user.login.UserLoginType;
import com.corpgovernment.api.permission.vo.GetUserPermissionListRequest;
import com.corpgovernment.common.apollo.OrganizationApollo;
import com.corpgovernment.common.base.BaseUserInfo;
import com.corpgovernment.common.base.CommonException;
import com.corpgovernment.common.base.Page;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.constant.CommonConst;
import com.corpgovernment.common.entity.db.BaseEntity;
import com.corpgovernment.common.enums.CardEnum;
import com.corpgovernment.common.handler.ICacheHandler;
import com.corpgovernment.common.holder.TenantContextHolder;
import com.corpgovernment.common.utils.ABTestUtil;
import com.corpgovernment.common.utils.Md5Util;
import com.corpgovernment.organization.constant.OrganizationResponseCodeEnum;
import com.corpgovernment.organization.constant.PermissionConstant;
import com.corpgovernment.organization.constant.RedisCacheTimeConst;
import com.corpgovernment.organization.dto.UserLoginDTO;
import com.corpgovernment.organization.entity.db.MbOrgEmployeeInfo;
import com.corpgovernment.organization.entity.db.MbOrgUserIdcard;
import com.corpgovernment.organization.entity.db.MbUserLogin;
import com.corpgovernment.organization.enumm.ERedisKeyType;
import com.corpgovernment.organization.factory.LoginServiceFactory;
import com.corpgovernment.organization.factory.LoginSourceServiceFactory;
import com.corpgovernment.organization.mapper.MbOrgEmployeeMapper;
import com.corpgovernment.organization.mapper.MbUserLoginMapper;
import com.corpgovernment.organization.producer.MessageEventProducer;
import com.corpgovernment.organization.service.IMbUserDeviceService;
import com.corpgovernment.organization.service.IOrgUserIdCardService;
import com.corpgovernment.organization.service.IUserLoginRecordService;
import com.corpgovernment.organization.service.IUserLoginService;
import com.corpgovernment.organization.service.LoginHelpService;
import com.corpgovernment.organization.service.MsgService;
import com.corpgovernment.organization.util.OrganizationUtils;
import com.corpgovernment.organization.vo.UserLoginRequestVO;
import com.corpgovernment.organization.vo.tenant.SearchTenantInfoRequestVO;
import com.corpgovernment.organization.vo.tenant.SearchTenantInfoResponseVO;
import com.corpgovernment.organization.vo.tenant.TenantInfoVO;
import com.corpgovernment.permission.entity.db.MbRole;
import com.corpgovernment.permission.entity.db.MbRolePermission;
import com.corpgovernment.permission.entity.db.MbUserRole;
import com.corpgovernment.permission.mapper.PermissionMapper;
import com.corpgovernment.permission.mapper.RoleMapper;
import com.corpgovernment.permission.mapper.RolePermissionMapper;
import com.corpgovernment.permission.mapper.UserRoleMapper;
import com.corpgovernment.permission.service.UserRoleService;
import com.corpgovernment.permission.service.UserService;
import com.corpgovernment.redis.cache.RedisUtils;
import com.corpgovernment.redis.cache.RoleResourceManager;
import com.corpgovernment.redis.handler.RedisHandler;
import com.ctrip.corp.obt.auth.client.handler.token.JwtTokenHandler;
import com.ctrip.corp.obt.auth.core.token.OAuth2AccessToken;
import com.ctrip.corp.obt.auth.core.token.definer.JwtPayload;
import com.ctrip.corp.obt.generic.core.context.RequestContext;
import com.ctrip.corp.obt.generic.core.context.TenantContext;
import com.ctrip.corp.obt.generic.notification.annotation.InfoChange;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.ExceptionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.shard.context.PageContext;
import com.ctrip.corp.obt.shard.sql.mybatis.interceptor.pagination.IPage;
import com.google.common.collect.ImmutableMap;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import tk.mybatis.mapper.entity.Example;

/**
 * @author ：llw
 * @date ：Created in 2020/12/15 19:21
 * @description：
 * @modified By：
 * @version: $
 */
@Service
@Slf4j
public class UserLoginServiceImpl implements IUserLoginService {

    private static final String REDISSON_BIND_PHONE_KEY = "REDISSON_BIND_PHONE_KEY";
    @Autowired
    private MbUserLoginMapper userLoginMapper;
    @Autowired
    private ICacheHandler cacheHandler;
    @Autowired
    private OrganizationApollo organizationApollo;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private MessageEventProducer messageEventProducer;
    @Autowired
    private UserService userService;
    @Autowired
    private IUserLoginRecordService userLoginRecordService;
    @Autowired
    private IMbUserDeviceService mbUserDeviceService;
    @Autowired
    private MsgService msgService;
    @Autowired
    private MbOrgEmployeeMapper orgEmployeeMapper;
    @Autowired
    private IOrgUserIdCardService orgUserIdCardService;
    @Autowired
    private LoginHelpService loginHelpService;
    @Autowired
    private LoginServiceFactory loginServiceFactory;
    @Autowired
    private LoginSourceServiceFactory loginSourceServiceFactory;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RoleResourceManager roleResourceManager;
    @Autowired
    private JwtTokenHandler tokenHandler;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private RolePermissionMapper rolePermissionMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RedisHandler redisHandler;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private JwtTokenHandler jwtTokenHandler;
    @Autowired
    @Qualifier(value = "defaultThreadPoolExecutor")
    private ThreadPoolExecutor defaultThreadPoolExecutor;
    @Autowired
    private ABTestUtil abTestUtil;
    /**
     * 根据手机号查询登录用户
     *
     * @param countryCode
     * @param phoneNum
     * @return
     */
    @Override
    public MbUserLogin findByPhone(String countryCode, String phoneNum) {

        MbUserLogin selectParam = new MbUserLogin();
        selectParam.setDeleteTime(new Date(0));
        selectParam.setCountryCode(countryCode);
        selectParam.setPhoneNum(phoneNum);
        selectParam.setBgUser(0);
        return this.selectOne(selectParam);
    }

    public MbUserLogin selectOne(MbUserLogin selectParam) {
        return userLoginMapper.selectOne(selectParam);
    }

    /**
     * 插入登录用户
     *
     * @param userLogin
     * @return
     */
    @Override
    public boolean create(MbUserLogin userLogin) {
        return this.insertSelective(userLogin) > 0;
    }

    public int insertSelective(MbUserLogin userLogin) {
        return userLoginMapper.insertSelective(userLogin);
    }

    /**
     * 根据UID删除登录用户
     *
     * @param Uid
     * @return
     */
    @Override
    public boolean deleteByUid(String Uid) {
        return userLoginMapper.deleteByUid(Uid) > 0;
    }

    /**
     * 查询未删除的数据
     *
     * @param selectParam
     * @return
     */
    @Override
    public List<MbUserLogin> list(MbUserLogin selectParam) {
        selectParam.setDeleteTime(new Date(0));
        return this.select(selectParam);
    }

    public List<MbUserLogin> select(MbUserLogin selectParam) {
        return userLoginMapper.select(selectParam);
    }

    /**
     * 根据uid查询
     *
     * @param uid
     * @return
     */
    @Override
    public MbUserLogin getByUid(String uid) {
        MbUserLogin selectParam = new MbUserLogin();
        selectParam.setUid(uid);
        return this.findOne(selectParam);
    }

    @Override
    public List<MbUserLogin> getByUids(List<String> uids) {
        Example example = new Example(MbUserLogin.class);
        example.createCriteria().andIn("uid", uids).andEqualTo("deleteTime", new Date(0));
        return userLoginMapper.selectByExample(example);
    }

    @Override
    public int updatePwd(MbUserLogin userLogin) {
        if (userLogin == null) {
            return 0;
        }
        return userLoginMapper.updatePwd(userLogin);
    }

    @Override
    public MbUserLogin getByPhoneNumAndCountryCodeAndBgUser(String phoneNum, String countryCode, Boolean bgUser) {
        MbUserLogin queryParam = new MbUserLogin();
        queryParam.setPhoneNum(phoneNum);
        queryParam.setCountryCode(countryCode);
        if (bgUser != null) {
            queryParam.setBgUser(bgUser ? 1 : 0);
        }
        return this.findOne(queryParam);
    }

    @Override
    public String validateUpdatePwd(String uid, String pwd) {
        log("validateUpdatePwd", uid, pwd);
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(1002, "用户id为空");
        }
        if (StringUtils.isBlank(pwd)) {
            throw new CommonException(1003, "请填写当前密码");
        }
        log.info("更新密码validateUpdatePwd,uId={},pwd{}", uid, pwd);
        // 根据uid查询用户
        MbUserLogin mbUserLogin = this.getByUid(uid);
        if (mbUserLogin == null) {
            // 用户不存在
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }
        if (!Md5Util.verifyDeepMd5Msg(mbUserLogin.getUid(), pwd, mbUserLogin.getPwd())) {
            // 密码不正确
            throw new CommonException(ELoginStatus.PWD_INCORRECT.getValue(), "请正确填写当前密码");
        }
        String validateToken = Md5Util.encodeMd5WithRandomSalt(uid + this.genMsgCode());
        cacheHandler.setValue(validateToken, uid, organizationApollo.getUpdatePwdValidTokenTimeOut() * 60L);
        return validateToken;
    }

    @Override
    public String resetPwdByUid(String uid) {
        MbUserLogin userLogin = this.getByUid(uid);
        log.info("用户信息：{}", JsonUtils.toJsonString(userLogin));
        if (Objects.isNull(userLogin)) {
            throw new CommonException(NOTFIND.getValue(), NOTFIND.getDesc());
        }
        String phoneNum = userLogin.getPhoneNum();
        if (StringUtils.isBlank(phoneNum) || phoneNum.length() < 6) {
            throw new CommonException(1000012, "当前用户手机号码无效，无法重置密码");
        }
        // 密码手机号后5位+4位随机数
        String pwd = buildNewPwd(phoneNum);
        userLogin.setPwd(Md5Util.getDeepMd5Msg(userLogin.getUid(), pwd));
        int result = userLoginMapper.updatePwd(userLogin);
        if (result <= 0) {
            throw new CommonException(1000012, "重置密码失败");
        }


        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            Map<String, Object> params = new HashMap<>(2);
            // 发送重置短信
            params.put("pwd", pwd);
            msgService.sendAliyunTemplateMsg(MsgBizType.EMPLOYEE_RESET_PWD, params,
                    userLogin.getPhoneNum(), userLogin.getCountryCode());
        }
        return avalibleSendMsg ? null : pwd;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean bindPhone(BaseUserInfo userInfo, UserLoginRequest request) {
        String countryCode = request.getCountryCode();
        String phoneNum = request.getPhoneNum();
        String msgCode = request.getMsgCode();
        String source = StringUtils.isBlank(request.getSource()) ? userInfo.getSource() : request.getSource();
        String accountName =
                StringUtils.isBlank(request.getAccountName()) ? userInfo.getAccountName() : request.getAccountName();
        // PC修改账号不需要验证码
        if (!Objects.equals(source, PC.name())) {
            bingPhoneByAPP(userInfo, countryCode, phoneNum, source, accountName,msgCode);
        }else {
            bingPhoneByPc(userInfo, countryCode, phoneNum, source, accountName);
        }
        return true;
    }

    private void bingPhoneByAPP(BaseUserInfo userInfo, String countryCode, String phoneNum, String source, String accountName,String msgCode) {
        msgService.validateMsgCode(ERedisKeyType.BIND_PHONE_CODE.getKey(countryCode + phoneNum), msgCode, source);

        RLock redLock = redissonClient.getLock(REDISSON_BIND_PHONE_KEY + phoneNum);
        int result = 0;
        try {
            if (redLock.tryLock(2, TimeUnit.SECONDS)) {
                bindPhoneNumValid(accountName, phoneNum, countryCode, false);
                MbUserLogin userLogin = this.getByAccountName(accountName);
                if (userLogin != null) {
                    // 修改登录表账号和手机
                    userLogin.setAccountName(countryCode + phoneNum);
                    userLogin.setCountryCode(countryCode);
                    userLogin.setPhoneNum(phoneNum);
                    result = this.updateByPrimaryKeySelective(userLogin);
                    // 修改员工表手机
                    orgEmployeeMapper.updatePhoneByUid(userLogin.getUid(), phoneNum);
                }
            }
        } catch (InterruptedException e) {
            log.error("获取{}锁发生异常", REDISSON_BIND_PHONE_KEY + phoneNum, e);
            Thread.currentThread().interrupt();
        } finally {
            redLock.unlock();
        }
        if (result <= 0) {
            throw new CommonException(100022, "手机帐号修改失败，请稍后重试");
        }
        String key = userInfo.getSource() + ":" + userInfo.getToken();
        BaseUserInfo cacheHandlerValue = (BaseUserInfo)cacheHandler.getValue(key);
        if(cacheHandlerValue!=null) {
            cacheHandlerValue.setAccountName(countryCode + phoneNum);
            cacheHandler.setValue(key, cacheHandlerValue, organizationApollo.getLoginTimeOut());
        }
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            Map<String, Object> vars = new HashMap<>();
            vars.put("BingPhoneNo", phoneNum);
            msgService.sendAliyunTemplateMsg(MsgBizType.UPDATE_USER_ACCOUNT,vars, phoneNum, countryCode);
        }

    }

    private void bingPhoneByPc(BaseUserInfo userInfo, String countryCode, String phoneNum, String source, String accountName) {
        RLock redLock = redissonClient.getLock(REDISSON_BIND_PHONE_KEY + phoneNum);
        String pwd = buildNewPwd(phoneNum);
        int result = 0;
        try {
            if (redLock.tryLock(2, TimeUnit.SECONDS)) {
                bindPhoneNumValid(accountName, phoneNum, countryCode, false);
                MbUserLogin userLogin = this.getByAccountName(accountName);
                if (userLogin != null) {
                    // 修改登录表账号和手机
                    userLogin.setAccountName(countryCode + phoneNum);
                    userLogin.setCountryCode(countryCode);
                    userLogin.setPhoneNum(phoneNum);
                    userLogin.setPwd(pwd);
                    result = this.updateByPrimaryKeySelective(userLogin);
                    // 修改员工表手机
                    orgEmployeeMapper.updatePhoneByUid(userLogin.getUid(), phoneNum);
                }
            }
        } catch (InterruptedException e) {
            log.error("获取{}锁发生异常", REDISSON_BIND_PHONE_KEY + phoneNum, e);
            Thread.currentThread().interrupt();
        } finally {
            redLock.unlock();
        }
        if (result <= 0) {
            throw new CommonException(100022, "手机帐号修改失败，请稍后重试");
        }
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            Map<String, Object> vars = new HashMap<>();
            vars.put("phone", phoneNum);
            vars.put("pwd", pwd);
            msgService.sendAliyunTemplateMsg(MsgBizType.EMPLOYEE_RESET_PHONE_AND_PWD,vars, phoneNum, countryCode);
        }

    }

    @InfoChange(group = "VALID_STAFF", identityExpression = "uid")
    public int updateByPrimaryKeySelective(MbUserLogin record) {
        return userLoginMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public String sendBindMsgCode(String accountName, String phoneNum, String countryCode, Boolean bgUser) {
        bindPhoneNumValid(accountName, phoneNum, countryCode, bgUser);
        String msgCode = this.genMsgCode();

        // 一分钟限流
        if (checkMinuteLimit(ERedisKeyType.BIND_PHONE_CODE_MINUTE_LIMIT.getKey(phoneNum))) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.SEND_CODE_LIMIT_PER_MINUTE_ERR);
        }

        cacheHandler.setValue(ERedisKeyType.BIND_PHONE_CODE.getKey(countryCode + phoneNum), msgCode,
                organizationApollo.getMsgTimeOut() * 60L);
        cacheHandler.setValue(ERedisKeyType.BIND_PHONE_CODE_MINUTE_LIMIT.getKey(phoneNum), msgCode, 60L);
        HashMap<String, Object> params = new HashMap<>(16);
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, msgCode);

        Boolean avalibleSendMsg = abTestUtil.getBooleanAbTestResultWithAuto("sendSms", TenantContext.getTenantId());
        if (avalibleSendMsg) {
            msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, phoneNum, countryCode);
        }
        return avalibleSendMsg ? null : msgCode;
    }

    @Override
    public String unBindPhone(String accountName, String phoneNum, String msgCode, String countryCode, String source) {
        log.info("解绑手机号accountName={},phoneNum={},msgCode={},countryCode={},source={}", accountName, phoneNum, msgCode,
                countryCode, source);
        msgService.validateMsgCode(ERedisKeyType.UNBIND_PHONE_CODE.getKey(countryCode + phoneNum), msgCode, source);
        int result = userLoginMapper.updateUnBindPhone(accountName);
        if (result <= 0) {
            throw new CommonException(100012, "解绑手机号失败");
        }
        return null;
    }

    @Override
    public String sendUploadStaffMsgCode(String accountName, String phoneNum, String countryCode, String source) {
        if (!Pattern.matches("^1[3-9]\\d{9}$", phoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }
        String msgCode = this.genMsgCode();
        cacheHandler.setValue(ERedisKeyType.UPLOAD_STAFF_PHONE_CODE.getKey(countryCode + phoneNum), msgCode,
                organizationApollo.getMsgTimeOut() * 60L);
        HashMap<String, Object> params = new HashMap<>(2);
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, msgCode);
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if(avalibleSendMsg){
            boolean flag = msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, phoneNum, countryCode);
            if (!flag) {
                throw new CorpBusinessException(OrganizationResponseCodeEnum.SMS_SEND_ERROR);
            }
        }
        return avalibleSendMsg ? null : msgCode;
    }

    @Override
    @InfoChange(group = "VALID_STAFF", identityExpression = "uid")
    public Boolean checkUploadStaffMsgCode(String accountName, String phoneNum, String mesCode, Boolean isSyncLoginPhone, String countryCode, String source) {
        if (!Pattern.matches("^1[3-9]\\d{9}$", phoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }

        // 验证解绑的验证码
        msgService.validateMsgCode(ERedisKeyType.UPLOAD_STAFF_PHONE_CODE.getKey(countryCode + phoneNum), mesCode, source);


        MbUserLogin userLogin = this.getByAccountName(accountName);
        if (userLogin == null) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.UID_NULL);
        }
        // 修改员工表手机
        orgEmployeeMapper.updatePhoneByUid(userLogin.getUid(), phoneNum);

        if(isSyncLoginPhone){
            bindPhoneNumValid(accountName, phoneNum, countryCode, false);
            // 修改登录表账号和手机
            userLogin.setAccountName(countryCode + phoneNum);
            userLogin.setCountryCode(countryCode);
            userLogin.setPhoneNum(phoneNum);
            this.updateByPrimaryKeySelective(userLogin);
        }

        return true;
    }


    @Override
    @InfoChange(group = "VALID_STAFF", identityExpression = "uid")
    public Boolean testNetty(String accountName) {
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @InfoChange(group = "VALID_STAFF", identityExpression = "uid")
    public UserLoginResponseVo changeBindPhone(String accountName, String unBindMsgCode, String unBindCountryCode, String unBindPhoneNum, String bindMsgCode, String bindCountryCode, String bindPhoneNum,String source){

        if (!Pattern.matches("^1[3-9]\\d{9}$", unBindPhoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }

        if (!Pattern.matches("^1[3-9]\\d{9}$", bindPhoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }
        // 验证解绑的验证码
        msgService.validateMsgCode(ERedisKeyType.UNBIND_PHONE_CODE.getKey(unBindCountryCode + unBindPhoneNum), unBindMsgCode, source);
        // 验证绑定的验证码
        msgService.validateMsgCode(ERedisKeyType.BIND_PHONE_CODE.getKey(bindCountryCode + bindPhoneNum), bindMsgCode, source);

        int result = userLoginMapper.updateUnBindPhone(accountName);
        if (result <= 0) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.CHANGE_PHONE_FAIL,"解绑失败");
        }

        RLock redLock = redissonClient.getLock(REDISSON_BIND_PHONE_KEY + bindPhoneNum);
        int changeResult = 0;
        MbUserLogin userLogin = new MbUserLogin();
        try {
            if (redLock.tryLock(2, TimeUnit.SECONDS)) {
                bindPhoneNumValid(accountName, bindPhoneNum, bindCountryCode, false);
                userLogin = this.getByAccountName(accountName);
                if (userLogin != null) {
                    // 修改登录表账号和手机
                    userLogin.setAccountName(bindCountryCode + bindPhoneNum);
                    userLogin.setCountryCode(bindCountryCode);
                    userLogin.setPhoneNum(bindPhoneNum);
                    changeResult = this.updateByPrimaryKeySelective(userLogin);
                    // 修改员工表手机
                    orgEmployeeMapper.updatePhoneByUid(userLogin.getUid(), bindPhoneNum);
                }
            }
        } catch (InterruptedException e) {
            log.error("获取{}锁发生异常", REDISSON_BIND_PHONE_KEY + bindPhoneNum, e);
        } finally {
            redLock.unlock();
        }
        if (changeResult <= 0) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.CHANGE_PHONE_FAIL,"绑定新号码失败");
        }
        String clientId = TenantContext.getClientId();
        if (clientId == null) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PARAMS_IS_NULL, "clientId");
        }

        // todo 自登录逻辑
      /*  MbUserLogin finalUserLogin = userLogin;
        OAuth2AccessToken oAuth2AccessToken = ExceptionUtils.catchThrow(
                () -> jwtTokenHandler.createAccessTokenByPwd(
                        clientId,
                        accountName,
                        finalUserLogin.getPwd(),
                        false,
                        Collections.singletonMap("source",source)),
                exception -> {
                    throw new CommonException(exception.getMessage());
                });
        log.info("create access token info:{} by pwd", JsonUtils.toJsonString(oAuth2AccessToken));
        Map<String, Object> userInfoByAccessToken = ExceptionUtils.catchThrow(
                () -> jwtTokenHandler.getUserInfoByAccessToken(clientId, oAuth2AccessToken.getValue()),
                exception -> {
                    throw new CommonException(exception.getMessage());
                });
        BaseUserInfo userInfo = JsonUtils.convert(userInfoByAccessToken, BaseUserInfo.class);
        userInfo.setToken((String) userInfoByAccessToken.get("id"));
        UserLoginResponseVo response = new UserLoginResponseVo();
        response.setToken(oAuth2AccessToken.getValue())
                .setUserName(userInfo.getUserName())
                .setUid(userInfo.getUid())
                .setAccountName(accountName)
                .setBgUser(userInfo.getBgUser())
                .setSource(userInfo.getSource())
                .setCid((String) userInfoByAccessToken.get("cid"))
                .setShowManage(false)
                .setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue())
                .setExpiration(oAuth2AccessToken.getExpiration());
        return loginSourceServiceFactory.getLoginSourceService(source).process(userInfo, response, LoginTypeEnum.PASSWORD);*/

        return null;
    }



    @Override
    public String sendUnbindMsg(String accountName, String phoneNum, String countryCode) {
        if (!Pattern.matches("^1[3-9]\\d{9}$", phoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }
        MbUserLogin mbUserLogin = this.getByAccountName(accountName);
        String userCountryCode = Optional.ofNullable(mbUserLogin).map(MbUserLogin::getCountryCode)
                .orElseThrow(() -> new CorpBusinessException(OrganizationResponseCodeEnum.ACCOUNT_OR_COUNTRY_CODE_IS_NULL));
        String userPhoneNum = Optional.of(mbUserLogin).map(MbUserLogin::getPhoneNum).filter(k-> phoneNum.equals(k))
                .orElseThrow(() -> new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_DONT_EXISTS));
        if (!StringUtils.equals(phoneNum, userPhoneNum, false)
                || !StringUtils.equals(countryCode, userCountryCode, false)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.ACCOUNT_OR_COUNTRY_CODE_ERROR);
        }
        String msgCode = this.genMsgCode();
        if (checkMinuteLimit(ERedisKeyType.UNBIND_PHONE_CODE_MINUTE_LIMIT.getKey(phoneNum))){
            throw new CorpBusinessException(OrganizationResponseCodeEnum.SEND_CODE_LIMIT_PER_MINUTE_ERR);
        }

        cacheHandler.setValue(ERedisKeyType.UNBIND_PHONE_CODE.getKey(countryCode + phoneNum), msgCode,
                organizationApollo.getMsgTimeOut() * 60L);
        cacheHandler.setValue(ERedisKeyType.UNBIND_PHONE_CODE_MINUTE_LIMIT.getKey(phoneNum), msgCode, 60L);

        HashMap<String, Object> params = new HashMap<>(2);
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, msgCode);

        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, phoneNum, countryCode);
        }
        return avalibleSendMsg ? null : msgCode;
    }

    @Override
    public UserLoginResponseVo validateResetPwdMsgCode(MbUserLoginVo request) {
        String phoneNum = request.getPhoneNum();
        String msgCode = request.getMsgCode();
        String source = request.getSource();
        String countryCode = request.getCountryCode();
        log("validateResetPwdMsgCode", phoneNum, msgCode, source);
        log.info("验证重置密码手机号短信验证码 phoneNum={},msgCode={}", phoneNum, msgCode);
        msgService.validateMsgCode(ERedisKeyType.REST_PWD_CODE.getKey(countryCode + phoneNum), msgCode, source);
        MbUserLogin mbUserLogin =
                this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, this.isBgUser(source));
        if (Objects.isNull(mbUserLogin)) {
            // 用户不存在
            throw new CommonException(NOTFIND.getValue(), NOTFIND.getDesc());
        }
        // 根据手机号生成token
        String validateToken = Md5Util.encodeMd5WithRandomSalt(phoneNum + this.genMsgCode());
        log.info("验证重置手机短信验证码成功 phoneNum={},validateToken={}", phoneNum, validateToken);
        // 设置缓存
        cacheHandler.setValue(ERedisKeyType.REST_PWD_SEL_TOKEN.getKey(countryCode + phoneNum), validateToken,
                organizationApollo.getMsgTimeOut() * 60L);
        UserLoginResponseVo response = new UserLoginResponseVo();
        response.setValidateToken(validateToken);
        response.setAccountName(mbUserLogin.getAccountName());
        return response;
    }

    @Override
    public String sendResetPwdMsgCode(MbUserLoginVo request) {
        String phoneNum = request.getPhoneNum();
        String countryCode = request.getCountryCode();
        String source = request.getSource();
        log("sendResetPwdMsgCode", phoneNum, source);
        MbUserLogin mbUserLogin = this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, isBgUser(source));
        if (Objects.isNull(mbUserLogin)) {
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }
        String msgCode = this.genMsgCode();
        log.info("发送【重置密码】验证码 msgCode={}", msgCode);

        // 发送重置验证码限流
        String key = ERedisKeyType.REST_PWD_CODE_MINUTE_LIMIT.getKey(phoneNum);
        Object cacheHandlerValue = cacheHandler.getValue(ERedisKeyType.REST_PWD_CODE_MINUTE_LIMIT.getKey(phoneNum));
        log.info("限流key:{},value:{}", key, cacheHandlerValue);
        if (checkMinuteLimit(ERedisKeyType.REST_PWD_CODE_MINUTE_LIMIT.getKey(phoneNum))){
            throw new CorpBusinessException(OrganizationResponseCodeEnum.SEND_CODE_LIMIT_PER_MINUTE_ERR);
        }

        Map<String, Object> params = new HashMap<>();
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, msgCode);
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            boolean flag = msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, phoneNum, countryCode);
            if (!flag) {
                throw new CorpBusinessException(OrganizationResponseCodeEnum.SMS_SEND_ERROR);
            }
            cacheHandler.setValue(ERedisKeyType.REST_PWD_CODE.getKey(countryCode + phoneNum), msgCode,
                    organizationApollo.getMsgTimeOut() * 60L);
            cacheHandler.setValue(ERedisKeyType.REST_PWD_CODE_MINUTE_LIMIT.getKey(phoneNum),msgCode,60L);
        }
        return avalibleSendMsg ? null : msgCode;

    }

    @Override
    public Boolean updatePassword(UpdatePasswordRequest request,BaseUserInfo userInfo) {
        String account = request.getAccount();
        String newPassword = request.getNewPassword();
        String oldPassword = request.getOldPassword();
        String uid = request.getUid();
        if (StringUtils.isBlank(account)) {
            throw new CommonException(1002, "用户id为空");
        }
        checkPwdStrong(newPassword);
        if (!newPassword.equals(request.getRePwd())) {
            throw new CommonException("两次密码输入不一致");
        }
        MbUserLogin accountLogin = this.getByUid(account);
        if (accountLogin == null) {
            // 用户不存在
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }
        if (!Md5Util.verifyDeepMd5Msg(account, oldPassword, accountLogin.getPwd())) {
            // 密码不正确
            throw new CommonException(ELoginStatus.PWD_INCORRECT.getValue(), "请正确填写当前密码");
        }
        // 根据uid登录
        MbUserLogin mbUserLogin = this.getByUid(uid);
        if (!uid.equals(account)) {
            if (mbUserLogin == null) {
                // 用户不存在
                throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
            }
//            if (mbUserLogin.getBgUser() != null && mbUserLogin.getBgUser() != 1) {
//                throw new CommonException("无权限修改密码");
//            }
        }
        // 生成密码
        String pwd = Md5Util.getDeepMd5Msg(account, newPassword);
        MbUserLogin param = new MbUserLogin();
        param.setUid(account);
        param.setPwd(pwd);
        int result = userLoginMapper.updatePwd(param);
        if (result <= 0) {
            throw new CommonException(PWD_UPDATE_FAIL.getValue(), "您的账号密码修改失败，请稍后重试");
        }

        // 根据用户不同发送不同内容的短信
        if(userInfo.getBgUser()){
            Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
            if (avalibleSendMsg) {
                Map<String, Object> params = new HashMap<>(2);
                // 发送重置短信
                params.put("pwd", newPassword);
                msgService.sendAliyunTemplateMsg(MsgBizType.EMPLOYEE_UPDATE_PWD, params,
                        accountLogin.getPhoneNum(), mbUserLogin.getCountryCode());

            }
        }else {
            Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
            if (avalibleSendMsg) {
                msgService.sendAliyunTemplateMsg(MsgBizType.UPDATE_USER_PASSWORD,new HashMap<>(),accountLogin.getPhoneNum(),mbUserLogin.getCountryCode());

            }
        }


        return true;
    }

    @Override
    public UserLoginResponseVo validateUpdatePassword(UpdatePasswordRequest request) {
        String uid = request.getUid();
        String account = request.getAccount();
        String oldPassword = request.getOldPassword();
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(1002, "用户未登录");
        }
        if (StringUtils.isBlank(oldPassword)) {
            throw new CommonException(1003, "请填写当前密码");
        }
        log.info("更新密码validateUpdatePwd,uId={},pwd{},account={}", uid, oldPassword, account);
        // 根据account查询用户
        MbUserLogin mbUserLogin = this.getByUid(account);
        if (mbUserLogin == null) {
            // 用户不存在
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }
        if (!Md5Util.verifyDeepMd5Msg(account, oldPassword, mbUserLogin.getPwd())) {
            // 密码不正确
            throw new CommonException(ELoginStatus.PWD_INCORRECT.getValue(), "请正确填写当前密码");
        }
        String validateToken = Md5Util.encodeMd5WithRandomSalt(uid + this.genMsgCode());
        cacheHandler.setValue(validateToken, uid, organizationApollo.getUpdatePwdValidTokenTimeOut() * 60L);

        UserLoginResponseVo response = new UserLoginResponseVo();
        response.setValidateToken(validateToken);
        return response;
    }

    @Override
    public Boolean updatePwd(BaseUserInfo baseUserInfo, UserLoginRequest request) {
        String uid = baseUserInfo.getUid();
        String newPwd = request.getNewPwd();
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(1002, "用户id为空");
        }

        // 根据uid登录
        MbUserLogin mbUserLogin = this.getByUid(uid);
        if (mbUserLogin == null) {
            // 用户不存在
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }

        if (!Md5Util.verifyDeepMd5Msg(uid, request.getPwd(), mbUserLogin.getPwd())) {
            // 密码不正确
            throw new CommonException(ELoginStatus.PWD_INCORRECT.getValue(), "请正确填写当前密码");
        }

        checkPwdStrong(newPwd);
        log.info("更新密码,uId={},newPwd{}", uid, newPwd);

        // 生成密码
        String pwd = Md5Util.getDeepMd5Msg(baseUserInfo.getUid(), newPwd);
        MbUserLogin param = new MbUserLogin();
        param.setUid(uid);
        param.setPwd(pwd);
        int result = userLoginMapper.updatePwd(param);
        if (result <= 0) {
            throw new CommonException(PWD_UPDATE_FAIL.getValue(), "您的账号密码修改失败，请稍后重试");
        }

        msgService.sendAliyunTemplateMsg(MsgBizType.UPDATE_USER_PASSWORD,new HashMap<>(),mbUserLogin.getPhoneNum(),mbUserLogin.getCountryCode());

        return true;
    }

    @Override
    public String sendMsgCode(MbUserLoginVo request) {
        String countryCode = request.getCountryCode();
        String phoneNum = request.getPhoneNum();
        if (!Pattern.matches("^1[3-9]\\d{9}$", phoneNum)) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.PHONE_NUM_INCORRECT_FORMAT);
        }
        String source = request.getSource();
        log("sendMsgCode", phoneNum, source);
        String systemType = organizationApollo.getSystemType();
        MbUserLogin mbUserLogin = this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, isBgUser(source));
        if (Objects.isNull(mbUserLogin)) {
            if (Objects.equals(systemType, zhongjin.getCode())) {
                throw new CommonException(ELoginStatus.ZHONGJINNOTFIND.getValue(),
                        ELoginStatus.ZHONGJINNOTFIND.getDesc());
            } else {
                throw new CorpBusinessException(OrganizationResponseCodeEnum.CUSTOM_CONTENT, "您输入的手机号未注册，请重新输入");
            }
        }
        if (Optional.ofNullable(mbUserLogin.getStatus()).orElse(0) != 1) {
            throw new CommonException(ACCOUNT_DISABLED.getValue(), ACCOUNT_DISABLED.getDesc());
        }

        //验证限制发送次数 ex：key为LOGIN_CODE_DAILY_LIMIT:+86178111111112024-01-01
        String loginCodeDailyLimitKey = ERedisKeyType.LOGIN_CODE_DAILY_LIMIT.getKey(countryCode + phoneNum + LocalDate.now());
        Object loginCodeDailyLimitValue = cacheHandler.getValue(loginCodeDailyLimitKey);
        boolean limitExceeded = Objects.nonNull(loginCodeDailyLimitValue) && Integer.parseInt(loginCodeDailyLimitValue.toString()) >= 6;
        if (limitExceeded) {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.VERIFICATION_CODE_SENT_EXCEEDS_LIMIT);
        }

        // 同手机号一分钟只能发送一次验证码
        String loginLimitKey = ERedisKeyType.LOGIN_CODE_MINUTE_LIMIT.getKey(phoneNum);
        if(checkMinuteLimit(loginLimitKey)){
            throw new CorpBusinessException(OrganizationResponseCodeEnum.SEND_CODE_LIMIT_PER_MINUTE_ERR);
        }

        String msgCode = this.genMsgCode();
        log.info("发送登录验证码 msgCode={}", msgCode);
        Map<String, Object> params = new HashMap<>(2);
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, msgCode);
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if (avalibleSendMsg) {
            boolean flag = msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, phoneNum, countryCode);
            if (flag) {
                //验证码失效时间  key：+8612345678901 value存储：msgCode$用户信息
                cacheHandler.setValue(ERedisKeyType.LOGIN_CODE.getKey(countryCode + phoneNum), msgCode.concat("&").concat(JsonUtils.toJsonString(mbUserLogin)),
                        organizationApollo.getMsgTimeOut() * 60L);
                // 设置1分钟频次限制key
                cacheHandler.setValue(ERedisKeyType.LOGIN_CODE_MINUTE_LIMIT.getKey(phoneNum), msgCode, 60L);
                //验证码每日限制
                if (Objects.nonNull(loginCodeDailyLimitValue)) {
                    cacheHandler.increment(loginCodeDailyLimitKey);
                } else {
                    DateTime date = DateUtil.date();
                    long seconds = DateUtil.between(date, DateUtil.endOfDay(date), DateUnit.SECOND);
                    cacheHandler.setValue(loginCodeDailyLimitKey, 1, seconds);
                }
            }
        }
        return avalibleSendMsg ? null : msgCode;



//		MessageVo messageVo = new MessageVo();
//		MessageVo.Sms sms = new MessageVo.Sms();
//		sms.setBizType(MsgBizType.SIGN_UP_SIGN_IN);
//		sms.setCountryCode(request.getCountryCode());
//		sms.setPhone(request.getPhoneNum());
//		Map<String, Object> vars = Maps.newHashMap();
//		vars.put("code", msgCode);
//		sms.setVars(vars);
//		messageVo.setSms(sms);
//		log.info("验证码发送短信phone:{},code:{}", request.getPhoneNum(), msgCode);
//		CompletableFuture.runAsync(() -> basicMessageClientLoader.sendMessage(messageVo));
    }

    /**
     *  根据key检验是否存在redis 存在不可访问
     * @param key
     * @return
     */
    private Boolean checkMinuteLimit(String key) {
        return Objects.nonNull(cacheHandler.getValue(key));
    }



    @Override
    public Boolean saveOrUpdate(MbUserLoginVo request, boolean bgUser) {
        String uid = request.getUid();
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(100021, "uid为空");
        }
        log("saveOrUpdateUserLogin", uid, bgUser, request);
        String phoneNum = request.getPhoneNum();
        String countryCode = request.getCountryCode();
        MbUserLogin mbUserLogin = this.getByUid(uid);
        log.info("saveOrUpdate 中 mbUserLogin：{}",JsonUtils.toJsonString(mbUserLogin));
        if (mbUserLogin == null) {
            mbUserLogin = this.getByAccountName(request.getAccountName());
            if (mbUserLogin != null) {
                throw new CommonException(1000011, "手机号已被占用,请使用其他手机号");
            }
            // 新增用户
            validPhoneNumAndCountryCode(phoneNum, countryCode, bgUser);

            Boolean flag = createUserLogin(request, bgUser);

            return flag;
        } else {
            Boolean flag = false;
            // 更新用户
            if (!(StringUtils.equals(mbUserLogin.getPhoneNum(), phoneNum, false) &&
                    StringUtils.equals(mbUserLogin.getCountryCode(), countryCode, false))) {
                // 手机号和区号发生变化 校验手机号和区号是被占用
                validPhoneNumAndCountryCode(phoneNum, countryCode, bgUser);
                request.setAccountName(countryCode+phoneNum); // +8618075374787

                flag = updateUserLogin(request, bgUser);
                // 更新完手机号后进行短信发送
                Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
                if(flag && avalibleSendMsg){
                    Map<String, Object> vars = new HashMap<>();
                    vars.put("BingPhoneNo",phoneNum);
                    msgService.sendAliyunTemplateMsg(MsgBizType.UPDATE_USER_ACCOUNT,vars,phoneNum,countryCode);
                }
            } else {
                // 手机号和区号未发生变化,忽略更新
                request.setPhoneNum(null);
                request.setCountryCode(null);
                flag = updateUserLogin(request, bgUser);

            }

            return flag;
        }
    }

    @Override
    public Boolean saveOrUpdate(MbUserLoginVo request, boolean bgUser, Boolean isSyncLoginPhone) {
        if(Boolean.TRUE.equals(isSyncLoginPhone)){
            return saveOrUpdate(request,bgUser);
        }
        // 新增员工，默认同步手机号
        if(isSyncLoginPhone == null){
            return saveOrUpdate(request,bgUser);
        }
        return false;
    }

    private String buildNewPwd(String phoneNum) {
        String newLast5 = phoneNum.substring(phoneNum.length() - 5);
        // 生成包含字母的随机数
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[4];
        random.nextBytes(bytes);
        String randomStr = Base64.getEncoder().encodeToString(bytes).substring(0, 4);
        return newLast5 + randomStr;
    }

    @Override
    public Boolean updateNameAndPhone(MbUserLoginVo request, boolean bgUser) {
        String uid = request.getUid();
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(100021, "uid为空");
        }
        log("saveOrUpdateUserLogin", uid, bgUser, request);
        MbUserLogin mbUserLogin = this.getByUid(uid);
        if (!(StringUtils.equals(mbUserLogin.getPhoneNum(), request.getPhoneNum(), false) &&
                StringUtils.equals(mbUserLogin.getCountryCode(), request.getCountryCode(), false))) {
            // 手机号和区号发生变化 校验手机号和区号是被占用
            validPhoneNumAndCountryCode(request.getPhoneNum(), request.getCountryCode(), bgUser);
        }
        mbUserLogin.setUserName(request.getUserName());
        mbUserLogin.setPhoneNum(request.getPhoneNum());
        mbUserLogin.setCountryCode(request.getCountryCode());
        return updateUserLogin(request, bgUser);
    }

    @Override
    public Boolean update(MbUserLogin updateParam) {
        if (updateParam == null || updateParam.getId() == null) {
            return false;
        }

        int update = this.updateByPrimaryKeySelective(updateParam);
        if (update <= 0) {
            throw new CommonException(1000122, "更新用户信息失败");
        }
        return true;
    }

    @Override
    public Boolean updateByUid(MbUserLogin updateParam) {
        Example example = new Example(MbOrgEmployeeInfo.class);
        example.createCriteria()
                .andEqualTo("uid", updateParam.getUid()).andEqualTo("deleteTime", new Date(0));
        return userLoginMapper.updateByExampleSelective(updateParam, example) > 0;
    }

    @Override
    public UserLoginResponseVo login(MbUserLoginVo request) {
        String loginType = request.getLoginType();
        loginType = Objects.isNull(loginType) ? LoginTypeEnum.PASSWORD.toString() : loginType;
        if (StringUtils.isNotBlank(request.getPwd())) {
            loginCheckPwdStrong(request.getPwd());
        }
        UserLoginResponseVo login = loginServiceFactory.getLoginService(loginType).login(request);
        // 缓存用户与权限的对应关系
        cacheUidRoleMap(login.getUid());
        return login;

    }

    /**
     * 获取corpCode
     *
     * @return
     */
    private String getCorpCode() {
        return "tenant";
    }

    /**
     * 获取当前所属group
     *
     * @return
     */
    private String getGroup() {
        return null;
    }

    /**
     * 获取当前tenantId
     *
     * @return
     */
    private String getTenantId() {
        return "tenant";
    }

    @Override
    public UserLoginResponseVo confirmLogin(UserLoginRequest request) {
        String source = request.getSource();
        String token = request.getToken();
//        BaseUserInfo userInfo = redisUtils.getCache(source + ":" + tokenKey, BaseUserInfo.class);
//        if (Objects.isNull(userInfo)) {
//            throw new CommonException(ELoginStatus.TOKEN_INVALID.getValue(), ELoginStatus.TOKEN_INVALID.getDesc());
//        }
//        cacheHandler.delete(source + ":" + tokenKey);
        Map<String, Object> userInfoMap = jwtTokenHandler.getUserInfoByAccessToken(TenantContext.getClientId(), token);
        BaseUserInfo userInfo = JsonUtils.convert(userInfoMap, BaseUserInfo.class);
        String uid = userInfo.getUid();
        String deptId = request.getDeptId();
        String corpId = request.getCorpId();
        String deptName = request.getDeptName();
        String corpName = request.getCorpName();
        String orgId = StringUtils.isBlank(deptId) ? corpId : deptId;
        // 查询用户组织信息,并判断当前组织公司是否可用,不可用不能登录,判断用户身份是否可用
        // 身份状态1正常，0待审核 只有正常状态可以登录
        UserLoginResponseVo response = new UserLoginResponseVo();
        if (!OrganizationUtils.checkIdentity(uid, orgId)) {
            throw new CommonException(100003, "用户身份认证失败");
        }
        userInfo.setOrgId(deptId).setOrgName(deptName).setCorpId(corpId).setCorpName(corpName);
//        tokenKey = genLoginToken(userInfo.getAccountName(), userInfo.getUid());
//        userInfo.setToken(tokenKey);
//        cacheHandler.setValue(source + ":" + tokenKey, userInfo, organizationApollo.getLoginTimeOut());
//        log.info("登录时长：{}", organizationApollo.getLoginTimeOut());
        Map<String, String> principal = new HashMap<>();
        principal.put("switchedOrgId", orgId);
        principal.put("userId", uid);
        OAuth2AccessToken reconfirmedToken = ExceptionUtils.catchThrow(
                () -> jwtTokenHandler.createAccessTokenByPwd(
                        TenantContext.getClientId(),
                        principal,
                        token,
                        false,
                        Collections.singletonMap("source", request.getSource())),
                exception -> new CommonException(exception.getMessage()));

        response.setToken(reconfirmedToken.getValue()).setUid(userInfo.getUid())
                .setAccountName(userInfo.getAccountName())
                .setUserName(userInfo.getUserName()).setBgUser(userInfo.getBgUser())
                .setIdentityShowStr(StringUtils.equals(corpId, deptId, false) ? corpName : corpName + ">" + deptName);
        if (Boolean.TRUE.equals(userInfo.getBgUser())) {
            // 后台登录获取菜单
            List<String> menuList = userService.getAllUrlList();
            response.setMenuList(menuList);
            return response;
        }
        JwtPayload jwtPayload = jwtTokenHandler.parseTokenPayload(reconfirmedToken.getValue(), JwtPayload.class);
        response.setShowManage(CollectionUtils.isNotEmpty(OrganizationUtils.getIdentityList(uid, deptId)));
        MbUserLoginRecordVo mbUserLoginRecordVo = new MbUserLoginRecordVo();
        mbUserLoginRecordVo.setUid(uid);
        mbUserLoginRecordVo.setCorpid(corpId);
        mbUserLoginRecordVo.setCorpName(corpName);
        mbUserLoginRecordVo.setOrgid(orgId);
        mbUserLoginRecordVo.setOrgName(deptName);
        mbUserLoginRecordVo.setSource(source);
        mbUserLoginRecordVo.setToken(jwtPayload.getTokenKey());
        mbUserLoginRecordVo.setUserName(userInfo.getUserName());
        log.info("用户确认身份登录(APP/PCBOOK)-------------,{}", JsonUtils.toJsonString(mbUserLoginRecordVo));
        userLoginRecordService.insert(mbUserLoginRecordVo);
        return response;
    }

    @Override
    public UserLoginResponseVo validateMsgCode(MbUserLoginVo request) {
        return loginServiceFactory.getLoginService(LoginTypeEnum.SMS.toString()).login(request);
    }

    @Override
    public UserLoginResponseVo updatePwdLogin(BaseUserInfo baseUserInfo, UserLoginRequest request) {
        String uid = baseUserInfo.getUid();
        String newPwd = request.getNewPwd();
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(1002, "用户id为空");
        }
        checkPwdStrong(newPwd);
        log.info("更新密码,uId={},newPwd{}", uid, newPwd);
        // 根据uid登录
        MbUserLogin mbUserLogin = this.getByUid(uid);
        if (mbUserLogin == null) {
            // 用户不存在
            throw new CommonException(ELoginStatus.NOTFIND.getValue(), ELoginStatus.NOTFIND.getDesc());
        }
        // 生成密码
        String pwd = Md5Util.getDeepMd5Msg(baseUserInfo.getUid(), newPwd);
        MbUserLogin param = new MbUserLogin();
        param.setId(mbUserLogin.getId());
        param.setPwd(pwd);
        boolean update = this.update(param);
        if (update) {
            // 登录
            MbUserLoginVo mbUserLoginVo = new MbUserLoginVo();
            mbUserLoginVo.setSource(baseUserInfo.getSource());
            mbUserLoginVo.setAccountName(baseUserInfo.getAccountName());
            mbUserLoginVo.setCountryCode(request.getCountryCode());
            mbUserLoginVo.setPwd(newPwd);
            return this.login(mbUserLoginVo);
        }
        throw new CommonException(PWD_UPDATE_FAIL.getValue(), "您的账号密码修改失败，请稍后重试");
    }

    @Override
    public UserLoginResponseVo resetPwdMsgCode(UserLoginRequest request) {
        String phoneNum = request.getPhoneNum();
        String countryCode = request.getCountryCode();
        String validateToken = request.getValidateToken();
        String source = request.getSource();

        checkPwdStrong(request.getNewPwd());

        log("resetPwdMsgCode", request);
        String newPwd = request.getNewPwd();
        String token = (String)cacheHandler.getValue(ERedisKeyType.REST_PWD_SEL_TOKEN.getKey(countryCode + phoneNum));
        if (StringUtils.isBlank(token) || !token.equals(validateToken)) {
            log.info("根据token和选择的uid重置密码失败 validateToken={} 已经失效", validateToken);
            throw new CommonException(ELoginStatus.TOKEN_INVALID.getValue(), ELoginStatus.TOKEN_INVALID.getDesc());
        }
        MbUserLogin mbUserLogin = this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, isBgUser(source));
        if (Objects.isNull(mbUserLogin)) {
            throw new CommonException(NOTFIND.getValue(), NOTFIND.getDesc());
        }
        // 生成密码
        String accountName = mbUserLogin.getAccountName();
        String pwd = Md5Util.getDeepMd5Msg(mbUserLogin.getUid(), newPwd);
        mbUserLogin.setPwd(pwd);
        boolean update = this.update(mbUserLogin);
        if (update) {
            MbUserLoginVo request1 = new MbUserLoginVo();
            request1.setAccountName(accountName);
            request1.setPwd(newPwd);
            request1.setSource(source);
            UserLoginResponseVo userLoginResponseVo = validAccountAndGenerateResponse(request1, ACCOUNT);
            CompletableFuture
                    .runAsync(
                            () -> messageEventProducer.sendMessageEvent(MsgBizType.EMPLOYEE_RESET_PWD,
                                    request.getCountryCode(), request.getPhoneNum(), ImmutableMap.of("pwd", newPwd)),
                            defaultThreadPoolExecutor);
            return userLoginResponseVo;
        }
        throw new CommonException(PWD_UPDATE_FAIL.getValue(), PWD_UPDATE_FAIL.getDesc());
    }

    @Override
    @InfoChange(group = "VALID_STAFF", identityExpression = "sub")
    public Boolean logout(Map<String, Object> userInfo, String uuid, String deviceId) {
        String uid = userInfo.get("sub").toString();
        String tokenId = userInfo.get("id").toString();
        log("logout", uid, tokenId, uuid, userInfo);
        log.info("用户登出：{}", userInfo);
        log.info("删除绑定设备1:uid:{},uuid:{},deviceId:{}",uid,uuid,deviceId);
        try {
//            cacheHandler.delete(source + ":" + token);
            tokenHandler.recoveryToken();
        } catch (Exception e) {
            log.info(e.toString());
        }
        if (StringUtils.isNotBlank(deviceId)) {
            mbUserDeviceService.deleteBy(uid, null, null);
        }
        return true;
    }

    @Override
    public Boolean updateUserStatus(UserLoginRequestVO request, BaseUserInfo userInfo) {
        if (Objects.isNull(request.getStatus())) {
            return false;
        }

        // validate permission
        validatePermission(request, userInfo);

        MbUserLogin selectParam = new MbUserLogin();
        selectParam.setUid(request.getUid());
        selectParam.setDeleteTime(new Date(0));
        MbUserLogin mbUserLogin = selectOne(selectParam);
        if (Objects.isNull(mbUserLogin) || Objects.equals(mbUserLogin.getStatus(), request.getStatus())) {
            return false;
        }

        log.info("updateUserStatus request:{}", request);
        mbUserLogin.setStatus(request.getStatus());
        return this.updateByPrimaryKeySelective(mbUserLogin) > 0;
    }

    /**
     * validate permission
     *
     * @param request
     * @param userInfo
     */
    private void validatePermission(UserLoginRequestVO request, BaseUserInfo userInfo) {
        if (Boolean.TRUE.equals(userInfo.getBgUser())) {
            // bgUser no need validate
            return;
        }
        String requestUid = request.getUid();
        if (StringUtils.isBlank(requestUid)) {
            // set context's uid if no uid in request
            request.setUid(userInfo.getUid());
            return;
        }
        if (!requestUid.equals(userInfo.getUid())) {
            log.warn("Unauthorized Operation: User [{}] try to access data belonging to [{}]", userInfo.getUid(), requestUid);
            throw new CorpBusinessException(OrganizationResponseCodeEnum.DATA_OPERATION_NO_PERMISSION);
        }
    }

    @Override
    public Boolean updateDeviceId(BaseUserInfo userInfo, MbUserDeviceRequest request) {
        if (Objects.isNull(userInfo) || StringUtils.isBlank(userInfo.getToken())) {
            throw new CommonException("当前登录用户已过期");
        }
        log("updateDeviceId", userInfo.getUid(), userInfo.getToken(), request, userInfo);
        if (StringUtils.isBlank(request.getOsType())) {
            throw new CommonException("系统类型为空");
        }
        if (!"1".equals(request.getOsType()) && !"2".equals(request.getOsType())) {
            throw new CommonException("不支持的系统版本 1：Android；2：IOS；");
        }

        Map<String, Integer> appIdMap = JsonUtils.parseMap(organizationApollo.getAppPushMappingData());

        MbUserDeviceVo param = new MbUserDeviceVo();
        param.setUid(userInfo.getUid());
        param.setDeviceId(request.getDeviceId());
        param.setResource("APP");
        param.setOsVersion(request.getOsType());
        param.setUserName(userInfo.getUserName());
        param.setUuid(request.getUuid());
        param.setPackageName(request.getPackageName());
        param.setAppId(appIdMap.get(request.getPackageName()));
        mbUserDeviceService.saveOrUpdate(param);
        return true;
    }

    private UserLoginResponseVo validAccountAndGenerateResponse(MbUserLoginVo request, LoginType loginType) {
        MbUserLogin mbUserLogin;
        String accountName = null;
        String phoneNum = null;
        String countryCode = null;
        String source = request.getSource();
        log.info("当前登录用户租户id:{}", TenantContextHolder.getTenantId());
        switch (loginType) {
            // 账号登录
            case ACCOUNT:
                accountName = request.getAccountName();
                mbUserLogin = this.getByAccountNameAndBgUser(accountName, isBgUser(source));
                if (mbUserLogin == null) {
                    mbUserLogin = this.getByAccountNameAndBgUser("+86" + accountName, isBgUser(source));
                }
                break;
            // 短信登录
            case SMS:
                phoneNum = request.getPhoneNum();
                countryCode = request.getCountryCode();
                mbUserLogin = this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, isBgUser(source));
                break;
            default:
                throw new CommonException(ILLEGAL_LOGIN.getValue(), ILLEGAL_LOGIN.getDesc());
        }
        if (mbUserLogin == null) {
            throw new CommonException(PWD_INCORRECT.getValue(), PWD_INCORRECT.getDesc());
        }
        SourceEnum sourceEnum = SourceEnum.valueOf(source);
        // app验证码登录添加姓名校验
        if (Objects.equals(loginType, SMS) && sourceEnum == SourceEnum.APP) {
            String userName = request.getUserName();
            if (StringUtils.isBlank(userName)) {
                throw new CommonException(USERNAME_NULL.getValue(), USERNAME_NULL.getDesc());
            }
            if (!Objects.equals(mbUserLogin.getUserName(), userName)) {
                throw new CommonException(USERNAME_INCORRECT.getValue(), USERNAME_INCORRECT.getDesc());
            }
        }
        UserLoginResponseVo responseVo = new UserLoginResponseVo();
        // 账号是否被禁用
        Boolean isEnable = Optional.of(mbUserLogin).map(MbUserLogin::getStatus).map(e -> e == 1).orElse(false);
        if (!isEnable) {
            throw new CommonException(ACCOUNT_DISABLED.getValue(), ACCOUNT_DISABLED.getDesc());
        }
        Integer bgUser = mbUserLogin.getBgUser();
        // 验证前后台用户登录
        boolean isBgUser = this.validateBgUser(bgUser, sourceEnum);
        String token = null;
        // 账号登录
        if (Objects.equals(ACCOUNT, loginType)) {
            loginHelpService.doCheck(request.getAccountName());
            String pwd = request.getPwd();
            if (!Md5Util.verifyDeepMd5Msg(mbUserLogin.getUid(), pwd, mbUserLogin.getPwd())) {
                loginHelpService.doFailure(request.getAccountName());
            }
            loginHelpService.doSuccess(request.getAccountName());
            token = this.genLoginToken(pwd, accountName);
        }
        // 验证码登录
        if (Objects.equals(SMS, loginType)) {
            String msgCode = request.getMsgCode();
            msgService.validateMsgCode(ERedisKeyType.LOGIN_CODE.getKey(countryCode + phoneNum), msgCode,
                    sourceEnum.name());
            token = this.genLoginToken(msgCode, countryCode + phoneNum);
        }
        // 验证密码复杂度
        // TODO
        // 响应信息和缓存信息填充
        String uid = mbUserLogin.getUid();
        String userName = mbUserLogin.getUserName();
        accountName = mbUserLogin.getAccountName();
        responseVo.setToken(token).setUserName(userName).setUid(uid).setAccountName(accountName).setBgUser(isBgUser)
                .setPhoneNum(mbUserLogin.getPhoneNum()).setCountryCode(mbUserLogin.getCountryCode())
                .setShowManage(false);
        BaseUserInfo userInfo = new BaseUserInfo().setUid(uid).setUserName(userName).setToken(token)
                .setBgUser(isBgUser).setAccountName(accountName).setSource(source).setDeviceId(request.getDeviceId())
                .setTenantId(TenantContextHolder.getTenantId());
        if (sourceEnum != PC) {
            MbOrgEmployeeInfo employeeInfo = orgEmployeeMapper.findByUid(uid);
            if (employeeInfo == null) {
                throw new CommonException(USERNAME_INCORRECT.getValue(), USERNAME_INCORRECT.getDesc());
            }
            String name = employeeInfo.getName();
            userInfo.setUserName(name);
            responseVo.setUserName(name);
            // 前台用户查询身份
            Map<Integer, List<OrgDTO>> integerListMap = OrganizationUtils.genUserOrgInfo(uid);
            responseVo.setLogined(false);
            responseVo.setNoOrgFlag(false);
            if (OrganizationUtils.checkIdentity(1, integerListMap)) {
                // 有且仅有一个审核通过的身份直接登录成功标志
                responseVo.setLogined(true);
                List<OrgDTO> orgInfos = integerListMap.get(1);
                String corpId = orgInfos.get(0).getCorpId();
                String deptId = orgInfos.get(0).getDeptId();
                String corpName = orgInfos.get(0).getCorpName();
                String deptName = orgInfos.get(0).getDeptName();
                userInfo.setCorpId(corpId).setOrgId(deptId).setCorpName(corpName).setOrgName(deptName);
                responseVo.setIdentityList(null).setCorpId(corpId).setOrgId(deptId).setCorpName(corpName)
                        .setOrgName(deptName);
                responseVo.setIdentityShowStr(
                        StringUtils.equals(corpId, deptId, false) ? corpName : corpName + ">" + deptName);
//                cacheHandler.setValue(source + ":" + token, userInfo, organizationApollo.getLoginTimeOut());
                log.info("登录时长：{}", organizationApollo.getLoginTimeOut());
                if (CollectionUtils.isNotEmpty(OrganizationUtils.getIdentityList(uid, deptId))) {
                    responseVo.setShowManage(true);
                }
                MbUserLoginRecordVo mbUserLoginRecordVo = new MbUserLoginRecordVo();
                mbUserLoginRecordVo.setUid(uid);
                mbUserLoginRecordVo.setCorpid(corpId);
                mbUserLoginRecordVo.setCorpName(corpName);
                mbUserLoginRecordVo.setOrgid(deptId);
                mbUserLoginRecordVo.setOrgName(deptName);
                mbUserLoginRecordVo.setSource(source);
                mbUserLoginRecordVo.setToken(token);
                mbUserLoginRecordVo.setUserName(userName);
                log.info("验证用户-------------,{}", JsonUtils.toJsonString(mbUserLoginRecordVo));
                userLoginRecordService.insert(mbUserLoginRecordVo);
            } else if (OrganizationUtils.checkIdentity(2, integerListMap)) {
                checkApprovalStatus(integerListMap.get(2));
                responseVo.setIdentityList(this.convert(integerListMap.get(2), "待审核"));
//                String key = source + ":" + token;
//                cacheHandler.setValue(key, userInfo, 60L);
//                log.info("校验验证码塞入缓存的key：" + key);
            } else if (OrganizationUtils.checkIdentity(-1, integerListMap) ||
                    OrganizationUtils.checkIdentity(0, integerListMap)) {
                // 没有审核通过身份需要跳转到注册页面标识
                responseVo.setNoOrgFlag(true);
                checkApprovalStatus(integerListMap.get(0));
                List<OrgDTO> orgVoS = integerListMap.get(0);
                MbUserRegisterRequestVO mbUserRegisterRequestVO =
                        new MbUserRegisterRequestVO().setUid(uid).setAccountName(accountName).setSource(source)
                                .setRoleList(new HashSet<>(this.convert(orgVoS, "待审核")));
                // 用户信息保存到缓存
                cacheHandler.setValue(ERedisKeyType.USER_ORG_REGISTER_TOKEN.getKey(accountName),
                        mbUserRegisterRequestVO, organizationApollo.getUserRegisterValidTokenTimeOut() * 60L);
                String validToken = UUID.randomUUID().toString();
                cacheHandler.setValue(USER_REGISTER_VALID_TOKEN.getKey(validToken), validToken,
                        organizationApollo.getUserRegisterValidTokenTimeOut() * 60L);
                responseVo.setValidToken(validToken);
            }
            log.info("用户登录uid登录成功 accountName={},token={},是否是后台用户={},身份信息={}}", accountName, token, bgUser,
                    integerListMap);
            return responseVo;
        }
        if (isBgUser) {
            // 菜单列表(后台才有)
            List<String> menuList = userService.getUserPermissionUrlList(GetUserPermissionListRequest.create(uid, null));
            responseVo.setMenuList(menuList);
            // 3个月
//            cacheHandler.setValue(source + ":" + token, userInfo, organizationApollo.getLoginTimeOut());
            log.info("用户登录uid登录成功 accountName={},token={},是否是后台用户={},menuList={}", accountName, token, bgUser,
                    menuList);
            return responseVo;
        }
        // 前台用户
        // 前台用户有效的身份
        List<OrgDTO> orgList = OrganizationUtils.getOrgInfoByUid(uid);
        // 前台用户有管理权限的身份
        List<OrgDTO> adminOrgList = orgList.stream()
                .filter(e -> CollectionUtils.isNotEmpty(OrganizationUtils.getIdentityList(uid, e.getDeptId())))
                .distinct().collect(Collectors.toList());
        // 无管理员权限，限制登录
        if (CollectionUtils.isEmpty(adminOrgList)) {
            throw new CommonException(NO_ADMIN_PERMISSION.getValue(), NO_ADMIN_PERMISSION.getDesc());
        } else if (adminOrgList.size() == 1) {
            // 有且仅有一个管理员权限，直接登录
            OrgDTO orgInfo = adminOrgList.get(0);
            userInfo.setCorpId(orgInfo.getCorpId()).setOrgId(orgInfo.getDeptId()).setCorpName(orgInfo.getCorpName())
                    .setOrgName(orgInfo.getDeptName());
            List<String> menuList =
                    userService.getAllUrlList();
            responseVo.setMenuList(menuList).setCorpId(orgInfo.getCorpId()).setOrgId(orgInfo.getDeptId())
                    .setCorpName(orgInfo.getCorpName()).setOrgName(orgInfo.getDeptName());
//            cacheHandler.setValue(source + ":" + token, userInfo, organizationApollo.getLoginTimeOut());
            log.info("用户登录uid登录成功 accountName={},token={},是否是后台用户={},menuList={}", accountName, token, bgUser,
                    menuList);
            return responseVo;
        } else {
            // 有多个管理员权限，让其选择身份登录
            responseVo.setIdentityList(this.convert(adminOrgList, "待审核"));
//            String key = source + ":" + token;
//            cacheHandler.setValue(key, userInfo, 60L);
        }
        return responseVo;
    }

    /**
     * 数据转换
     *
     * @param orgList
     * @param state
     * @return
     */
    private List<MbUserRegisterIdentityVO> convert(List<OrgDTO> orgList, String state) {
        if (CollectionUtils.isEmpty(orgList)) {
            return new ArrayList<>();
        }
        return orgList.stream().filter(Objects::nonNull).map(e -> {
            MbUserRegisterIdentityVO target = new MbUserRegisterIdentityVO();
            target.setDeptId(e.getDeptId());
            target.setDeptName(e.getDeptName());
            target.setCorpId(e.getCorpId());
            target.setCorpName(e.getCorpName());
            target.setState(e.getApprovalStatus() == 1 ? "审核通过" : state);
            target.setStatus(e.getApprovalStatus());
            return target;
        }).collect(Collectors.toList());
    }

    /**
     * 校验用户身份审核审核状态
     *
     * @param orgList
     */
    private void checkApprovalStatus(List<OrgDTO> orgList) {
        if (CollectionUtils.isEmpty(orgList)) {
            return;
        }
        for (OrgDTO org : orgList) {
            Optional.ofNullable(org).map(OrgDTO::getApprovalStatus)
                    .orElseThrow(() -> new CommonException(1100010, "用户身份审核状态为空"));
        }
    }

    /**
     * 生成登录token
     *
     * @param pwd 密码/验证码
     * @param uid 用户账号/手机号
     * @return token
     */
    private String genLoginToken(String pwd, String uid) {
        String tenantId = TenantContextHolder.getTenantId();
        return Md5Util.encodeMd5WithRandomSalt(tenantId + uid + pwd);
    }

    /**
     * 校验前后台用户 前台用户不可以登录后台,后台用户不可以登录前台
     *
     * @param bgUser 前后台用户 1:后台用户 0:前台用户
     * @param source 登录来源
     */
    private boolean validateBgUser(Integer bgUser, SourceEnum source) {
        if (Objects.isNull(bgUser)) {
            throw new CommonException(1000101, "无法确认用户身份");
        }
        // 后台用户不可以登录前台 1:后台用户 0:前台用户
        if (bgUser == 1) {
            // 后台用户
            if (!Objects.equals(source, PC)) {
                // 后台用户不可以登录前台
                throw new CommonException("用户不存在");
            }
            return true;
        } else {
            // 前台用户
            if (Objects.equals(source, PC)) {
                // 前台用户登录后台提示用户不存在
//				throw new CommonException("用户不存在");
            }
            return false;
        }
    }

    /**
     * 根据账号和是否后台用户查询
     *
     * @param accountName
     * @param bgUser
     * @return
     */
    private MbUserLogin getByAccountNameAndBgUser(String accountName, Boolean bgUser) {
        MbUserLogin queryParam = new MbUserLogin();
        queryParam.setAccountName(accountName);
        if (bgUser != null) {
            queryParam.setBgUser(bgUser ? 1 : 0);
        }
        return this.findOne(queryParam);
    }

    /**
     * 更新用户信息
     *
     * @param request 更新用户信息
     */
    private Boolean updateUserLogin(MbUserLoginVo request, boolean bgUser) {
        String uid = request.getUid();
        log("updateUserLogin", uid, bgUser, request);
        if (StringUtils.isBlank(uid)) {
            throw new CommonException(1002, "用户uid不能为空");
        }
        MbUserLogin param = new MbUserLogin();
        MbUserLogin mbUserLogin = this.getByUid(uid);
        BeanUtils.copyProperties(request, param);
        param.setId(mbUserLogin.getId());
        if (bgUser) {
            // 后台用户
            param.setBgUser(1);
        } else {
            param.setBgUser(0);
        }
        // 更改密码后不能立即生效
        if (!StringUtils.isBlank(request.getPwd())) {
            param.setPwd(Md5Util.getDeepMd5Msg(mbUserLogin.getUid(), request.getPwd()));
        }
        log.info("更新用户信息，用户信息：{}", JsonUtils.toJsonString(param));
        int update = this.updateByPrimaryKeySelective(param);
        log.info("更新用户信息，update：{}", update);
        if (update <= 0) {
            throw new CommonException(1000122, "更新用户信息失败");
        }
        return true;
    }

    /**
     * 创建用户
     */
    public Boolean createUserLogin(MbUserLoginVo request, boolean bgUser) {
        log("createUserLogin", request.getAccountName(), bgUser, request);
        String accountName = request.getAccountName();
        String uid = request.getUid();
        MbUserLogin param = new MbUserLogin();
        BeanUtils.copyProperties(request, param);
        if (StringUtils.isBlank(accountName)) {
            throw new CommonException(1000010, "账号名为空");
        }
        String pwd = request.getPwd();
        if (bgUser) {
            // 后台用户
            param.setBgUser(1);
            param.setPwd(Md5Util.getDeepMd5Msg(uid, request.getPwd()));
        } else {
            // 前台用户
            String cardNo = request.getCardNo();
            String phoneNum = request.getPhoneNum();
            if (StringUtils.isBlank(cardNo)) {
                throw new CommonException(10000010, "证件号不能为空");
            }
            if (StringUtils.isBlank(uid)) {
                throw new CommonException(10000010, "员工工号不能为空");
            }
            pwd = this.buildNewPwd(phoneNum);
            param.setBgUser(0);
            log.info("生成前台用户密码uid[{}] pwd[{}]", request.getUid(), pwd);
            param.setPwd(Md5Util.getDeepMd5Msg(uid, pwd));
            Map<String, String> keyValues = new HashMap<>(16);
            keyValues.put(TEMPLATE_VARIABLE_PASSWORD_NAME, pwd);
        }
        int insert = userLoginMapper.insertSelective(param);
        if (insert <= 0) {
            throw new CommonException(1000212, "创建用户失败");
        }
        String finalPwd = pwd;

        // 更新完手机号后进行短信发送
        Boolean avalibleSendMsg =  abTestUtil.getBooleanAbTestResultWithAuto("sendSms",TenantContext.getTenantId());
        if(avalibleSendMsg){
            Map<String, Object> vars = new HashMap<>();
            vars.put("phone", request.getPhoneNum());
            vars.put("pwd", finalPwd);
            msgService.sendAliyunTemplateMsg(MsgBizType.EMPLOYEE_INIT_PHONE_AND_PWD,vars, request.getPhoneNum(), request.getCountryCode());
        }

        return true;
    }

    /**
     * 根据证件号生成密码
     *
     * @param cardNo 证件号
     * @return 密码
     */
    private String genInitialString(String cardNo) {
        // 密码手机号后8位
        return StringUtils.substring(cardNo, cardNo.length() - 8, cardNo.length());
    }


    /**
     * 校验手机号和国际区码是否被占用
     *
     * @param phoneNum    手机号
     * @param countryCode 国际区码
     */
    private void validPhoneNumAndCountryCode(String phoneNum, String countryCode, boolean bgUser) {
        if (StringUtils.isNotBlank(phoneNum) && StringUtils.isBlank(countryCode)) {
            throw new CommonException(1000010, "phoneNum存在时,countryCode不能为空");
        }
        MbUserLogin phoneNumAndCountryCodeAndBgUser =
                this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, bgUser);
        if (Objects.nonNull(phoneNumAndCountryCodeAndBgUser)) {
            throw new CommonException(1000011, "手机号已被占用,请使用其他手机号");
        }
    }

    @Override
    public int updateUserLogin(MbUserLogin userLogin, boolean isCover) {

        if (userLogin == null || userLogin.getId() == null || userLogin.getId() <= 0) {
            return 0;
        }
        if (isCover) {
            String pwd = this.buildNewPwd(userLogin.getPhoneNum());
            userLogin.setPwd(Md5Util.getDeepMd5Msg(userLogin.getUid(), pwd));
        }

        return this.updateByPrimaryKeySelective(userLogin);
    }

    /**
     * 分页查询
     *
     * @param request
     * @return
     */
    @Override
    public Page<UserInfoType> pageByCondition(PageByConditionRequest request) {
        Integer status = request.getStatus();
        List<String> uids = new ArrayList<>();
        String cardNo = request.getCertificateNumber();
        // 如果有证件号，根据证件号获取用户id
        if (StringUtils.isNotBlank(cardNo)) {
            List<MbOrgUserIdcard> userCardList = orgUserIdCardService.getLikeCardNo(cardNo);
            uids.addAll(
                    userCardList.stream().map(MbOrgUserIdcard::getEmployeeUid).distinct().collect(Collectors.toList()));
        }
        // 获取配置的数据权限
        List<String> listOrgIds = getListOrgIds();
        log.info("查询参数为：{}", JsonUtils.toJsonString(request));
        List<MbUserLogin> userLoginList =
                userLoginMapper.listByRelationCondition(request.getAccount(), request.getEmail(), status, listOrgIds, uids);

        log.info("初次获取用户信息：{}", JsonUtils.toJsonString(userLoginList));
        if (StringUtils.isNotBlank(request.getName())) {
            userLoginList = userLoginList.stream().filter(e -> StringUtils.contains(e.getUserName(), request.getName()))
                    .collect(Collectors.toList());
        }
        log.info("初次过滤后的用户信息：{}", JsonUtils.toJsonString(userLoginList));
        List<String> userIds = userLoginList.stream().map(MbUserLogin::getUid).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userIds)) {
            return new Page<>();
        }

        Example example = new Example(MbUserLogin.class);
        example.createCriteria().andIn("uid", userIds)
                .andEqualTo("deleteTime", new Date(0))
                .andEqualTo("bgUser", 0);
        example.orderBy("datachangeLasttime").desc();
        IPage<MbUserLogin> page = PageContext.startPage(request.getPageNum(), request.getPageSize());
        userLoginMapper.selectByExample(example);
        userLoginList = page.getRecords();
        if (CollectionUtils.isEmpty(userLoginList)) {
            return new Page<>();
        }
        List<MbOrgEmployeeInfo> mbOrgEmployeeInfos = orgEmployeeMapper
                .listByUidsAllPlat(userLoginList.stream().map(MbUserLogin::getUid).collect(Collectors.toList()));
        Map<String, List<MbOrgEmployeeInfo>> employeeInfoMap = Optional.ofNullable(mbOrgEmployeeInfos)
                .orElse(new ArrayList<>()).stream().collect(Collectors.groupingBy(MbOrgEmployeeInfo::getUid));

        List<MbOrgUserIdcard> userCardList = orgUserIdCardService.getByUids(userIds);
        Map<String, List<MbOrgUserIdcard>> userCardMap =
                userCardList.stream().collect(Collectors.groupingBy(MbOrgUserIdcard::getEmployeeUid));
        List<UserInfoType> list = userLoginList.stream().map(e -> {
            UserInfoType target = new UserInfoType();
            target.setId(e.getId());
            target.setAccount(e.getAccountName());
            target.setName(e.getUserName());
            List<MbOrgUserIdcard> orgUserIdCards = userCardMap.get(e.getUid());
            if (CollectionUtils.isNotEmpty(orgUserIdCards)) {
                if (StringUtils.isNotBlank(cardNo)) {
                    orgUserIdCards = orgUserIdCards.stream().filter(o -> StringUtils.contains(o.getCardNo(), cardNo))
                            .collect(Collectors.toList());
                }
                // 取最新的证件
                orgUserIdCards.stream().max(Comparator.comparing(BaseEntity::getDatachangeLasttime)).ifPresent(o -> {
                    target.setCertificateType(CardEnum.getByType(String.valueOf(o.getCardType())).getName());
                    target.setCertificateNumber(o.getCardNo());
                });
            }
            if (employeeInfoMap.containsKey(e.getUid())) {
                target.setEmail(employeeInfoMap.get(e.getUid()).stream().findFirst().get().getEmail());
            }

            target.setStatusCode(e.getStatus() == 1 ? 1 : 2);
            return target;
        }).collect(Collectors.toList());

        return new Page<>((int)page.getCurrent(), (int)page.getSize(), (int)page.getPages(), page.getTotal(), list);
    }

    private List<String> getListOrgIds() {
        Object orgIds = RequestContext.getCurrentContext().getContextParams(DATA_PERMISSION_ORG_ID_LIST);
        List<String> listOrgIds = new ArrayList<>();
        if (Objects.nonNull(orgIds)) {
            String str = JsonUtils.toJsonString(orgIds);
            if (StringUtils.isNotBlank(str)) {
                log.info("获取到的数据权限为：{}", str);
                listOrgIds = JsonUtils.parseArray(str, String.class);
            }
        }
        return listOrgIds;
    }

    /**
     * 保存账号信息
     *
     * @param userLogin
     * @return
     */
    @Override
    public Boolean save(UserLoginType userLogin) {
        if (userLogin == null) {
            return false;
        }
        MbUserLogin target = this.convert(userLogin);
        target.setDatachangeCreatetime(new Date());
        target.setDatachangeLasttime(new Date());
        target.setDeleteTime(new Date(0));
        return this.insertSelective(target) > 0;
    }

    @Override
    public Boolean sendSmsMsg(SendMsgRequest request) {
        Map<String, Object> params = new HashMap<>();
        params.put(MsgService.TEMPLATE_VARIABLE_CODE_NAME, request.getCode());
        msgService.sendAliyunTemplateMsg(MsgBizType.UNIVERSAL_VERIFICATION_CODE, params, request.getPhone(),
                request.getCountryCode());
        return true;
    }

    @Override
    public SearchTenantInfoResponseVO searchTenantInfo(SearchTenantInfoRequestVO request) {
        TenantContext.setTenantId(CommonConst.DEFAULT_TENANT_ID);
        String tenantInfoStr = organizationApollo.getTenantInfo();
        if (StringUtils.isNotBlank(tenantInfoStr)) {
            List<TenantInfoVO> tenantInfos = JsonUtils.parseArray(tenantInfoStr, TenantInfoVO.class);
            return SearchTenantInfoResponseVO.create(tenantInfos);
        }
        return SearchTenantInfoResponseVO.create();
    }

    @Override
    public UserLoginResponseVo refresh(String refreshToken) {
        if (StringUtils.isBlank(refreshToken)) {
            throw new CommonException(1033, "No refreshToken was found");
        }
        String clientId = TenantContext.getClientId();
        if (clientId == null) {
            throw new CommonException(1031, "No client id was found");
        }
        UserLoginResponseVo response = new UserLoginResponseVo();
        OAuth2AccessToken oAuth2AccessToken = tokenHandler.refreshAccessToken(clientId, refreshToken);
        response.setToken(oAuth2AccessToken.getValue())
                .setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue())
                .setExpiration(oAuth2AccessToken.getExpiration());
        return response;
    }

    @Override
    public BaseUserInfo getUserInfo(GetUserInfoRequestBO request) {
        String source = request.getSource();
        return loginSourceServiceFactory.getLoginSourceService(source).getUserInfoV2(request);
    }

    @Override
    public BaseUserInfo getUserInfoV2(GetUserInfoRequestBO request) {
        String source = request.getSource();
        return loginSourceServiceFactory.getLoginSourceService(source).getUserInfoV2(request);
    }

    /**
     * 数据转换
     *
     * @param source
     * @return
     */
    private MbUserLogin convert(UserLoginType source) {
        if (source == null) {
            return null;
        }
        MbUserLogin target = new MbUserLogin();
        target.setAccountName(source.getAccountName());
        target.setUid(source.getUid());
        target.setPwd(source.getPwd());
        target.setUserName(source.getUserName());
        target.setBgUser(source.getBgUser());
        target.setCountryCode(source.getCountryCode());
        target.setPhoneNum(source.getPhoneNum());
        target.setEmail(source.getEmail());
        target.setStatus(source.getIsEnabled());
        target.setSetPasswordTime(source.getSetPasswordTime());
        return target;
    }

    private List<UserInfoType> convert(List<UserLoginDTO> sourceList) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        List<UserInfoType> result = new ArrayList<>();
        for (UserLoginDTO source : sourceList) {
            if (source == null) {
                return null;
            }
            UserInfoType target = new UserInfoType();
            target.setId(source.getId());
            target.setAccount(source.getAccountName());
            target.setName(source.getUserName());
            target.setCertificateType(source.getCertificateType());
            target.setCertificateNumber(source.getCertificateNumber());
            target.setEmail(source.getEmail());
            target.setStatusCode(source.getIsEnabled() == 1 ? 1 : 2);
            result.add(target);
        }
        return result;
    }

    /**
     * 根据账号查询
     *
     * @param accountName
     * @return
     */
    private MbUserLogin getByAccountName(String accountName) {
        MbUserLogin selectParam = new MbUserLogin();
        selectParam.setAccountName(accountName);
        return this.findOne(selectParam);
    }

    /**
     * 绑定手机号校验
     *
     * @param accountName 账户名
     * @param phoneNum    手机号
     * @param countryCode 国际区码
     */
    private void bindPhoneNumValid(String accountName, String phoneNum, String countryCode, boolean bgUser) {
        // 校验手机号是否可用
        if (StringUtils.isBlank(accountName)) {
            throw new CommonException(100021, "当前用户账户名为空");
        }
        MbUserLogin mbUserLogin = this.getByPhoneNumAndCountryCodeAndBgUser(phoneNum, countryCode, bgUser);
        if (mbUserLogin != null && !StringUtils.equals(mbUserLogin.getAccountName(), accountName, false)) {
            // 账号存在,且不是同一个账号
            throw new CommonException(100020, "登录帐号已存在，请重新输入");
        }
    }

    private void log(String methodName, Object... args) {
        if (args == null || args.length == 0) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            sb.append(methodName).append("=").append(arg).append(" ");
        }
        log.info(sb.toString());
    }

    /**
     * 生产验证码
     *
     * @return
     */
    private String genMsgCode() {
        return RandomStringUtils.randomNumeric(6);
    }

    /**
     * 获取第一条
     *
     * @param selectParam
     * @return
     */
    private MbUserLogin findOne(MbUserLogin selectParam) {
        List<MbUserLogin> list = Optional.ofNullable(this.list(selectParam)).orElse(new ArrayList<>());
        return list.stream().filter(Objects::nonNull).findFirst().orElse(null);
    }

    /**
     * 根据source判断是否bgUser
     *
     * @param source 来源
     * @return 是否bgUser
     */
    private Boolean isBgUser(String source) {
        Boolean bgUser;
        SourceEnum sourceEnum = SourceEnum.valueOf(source);
        switch (sourceEnum) {
            case PCBOOK:
            case APP:
                bgUser = false;
                break;
            case PC:
                bgUser = null;
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + sourceEnum);
        }
        return bgUser;
    }

    /**
     * 密码强度校验
     *
     * @param pwd 必须为6-20位字母或者数字或者特殊字符至少两种组合
     */
    private void checkPwdStrong(String pwd) {
        String regexp = "^(?![A-Z]*$)(?![a-z]*$)(?![0-9]*$)(?![^a-zA-Z0-9]*$)\\S{8,16}$";
        Pattern pattern = Pattern.compile(regexp);
        if (StringUtils.isBlank(pwd) || !pattern.matcher(pwd).matches()) {
            throw new CommonException(100012, "密码需至少包含数字、大写字母、小写字母、特殊字符中两种，及8-16位");
        }
    }

    private void loginCheckPwdStrong(String pwd) {
        if (StringUtils.isBlank(pwd) || pwd.length() < 8) {
            throw new CommonException(100012, "您输入的密码有误，请重新输入");
        }
    }

    /**
     * 缓存uid对应的角色，以及该角色拥有的权限
     *
     * @param uid
     */
    private void cacheUidRoleMap(String uid) {
        Example example = new Example(MbUserRole.class);
        example.createCriteria().andEqualTo("uid", uid);
        List<MbUserRole> mbUserRoleList = userRoleMapper.selectByExample(example);
        log.info("存入redis的uid：{} 对应role：{}", uid, JsonUtils.toJsonString(mbUserRoleList));
        if (CollectionUtils.isNotEmpty(mbUserRoleList)) {
            List<Long> roleList = mbUserRoleList.stream().map(MbUserRole::getRoleId).collect(Collectors.toList());
            Example mbRoleExample = new Example(MbRole.class);
            mbRoleExample.createCriteria().andIn("id", roleList);
            List<MbRole> mbRoles = roleMapper.selectByExample(mbRoleExample);
            List<Long> roleParentList =
                    mbRoles.stream().map(MbRole::getParentId).filter(Objects::nonNull).collect(Collectors.toList());
            roleList.addAll(roleParentList);
            roleList = roleList.stream().distinct().collect(Collectors.toList());
            // 将uid对应的角色存入
            redisHandler.setValue(PermissionConstant.UID_ROLE_RELATION + uid, roleList,
                    RedisCacheTimeConst.DEFAULT_CACHE_SECONDS);
            log.info("存入redis的uid：{} 对应role：{}", uid, roleList);
            // 将角色与权限的对应关系存入
            roleList.forEach(roleId -> {
                List<MbRolePermission> mbRolePermissionList = rolePermissionMapper.listByRoleId(roleId);
                List<Long> permissionList = mbRolePermissionList.stream()
                        .map(MbRolePermission::getPermissionId)
                        .collect(Collectors.toList());
                redisHandler.setValue(PermissionConstant.ROLE_PERMISSION_RELATION + roleId, permissionList,
                        RedisCacheTimeConst.DEFAULT_CACHE_SECONDS);
                log.info("存入redis的roleId：{} 对应permission：{}", roleId, JsonUtils.toJsonString(permissionList));

            });
        }
    }

}
